package cn.javabb.web.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import cn.javabb.core.redis.RedisProperties;
import cn.javabb.core.shiro.UserLoginFilter;
import cn.javabb.core.shiro.UserModularRealmAuthenticator;
import cn.javabb.core.shiro.realm.OAuthRealm;
import cn.javabb.core.shiro.realm.UserRealm;
import cn.javabb.web.config.AppConfig;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.annotation.Order;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Configuration
@Order(1)
public class ShiroConfig {

    @Autowired
    private RedisProperties redisProperties;

    @Autowired
    private AppConfig appConfig;

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        // 登录配置 ，如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilter.setLoginUrl("/error/401");
        shiroFilter.setSuccessUrl("/");
        shiroFilter.setUnauthorizedUrl("/error/403");
        // 自定义过滤器
        Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
        filtersMap.put("userLoginFilter", new UserLoginFilter());
        filtersMap.put("catalogFilter", new CatalogPermissionFilter());
        shiroFilter.setFilters(filtersMap);
        // 拦截配
        Map<String, String> filterChainDefinitions = new LinkedHashMap<String, String>();
        /*filterChainDefinitions.put("/assets/**", "anon");
        filterChainDefinitions.put("/module/**", "anon");
        filterChainDefinitions.put("/api/**", "anon");
        filterChainDefinitions.put("/loginForm/login", "anon");
        filterChainDefinitions.put("/login", "anon");
        filterChainDefinitions.put("/loginForm/register", "anon");
        filterChainDefinitions.put("/", "anon");
        filterChainDefinitions.put("/index", "anon");
        filterChainDefinitions.put("/catalog/**", "anon");
        filterChainDefinitions.put("/logout", "logout");
        filterChainDefinitions.put("/**", "userLoginFilter,authc");*/
        filterChainDefinitions.put("/blog/publish", "userLoginFilter,authc");
        filterChainDefinitions.put("/post/publish", "userLoginFilter,authc");
        filterChainDefinitions.put("/center/**", "userLoginFilter,authc");
        //filterChainDefinitions.put("/catalog/**", "catalogFilter");
        filterChainDefinitions.put("/logout", "logout");
        filterChainDefinitions.put("/**", "anon");
        shiroFilter.setFilterChainDefinitionMap(filterChainDefinitions);
        return shiroFilter;
    }

    /**
     * 用户账号Real
     * @return
     */
    @Bean
    public UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(credentialsMatcher());
        return userRealm;
    }

    /**
     * 第三方登陆Realm
     * @return
     */
    @Bean
    public OAuthRealm OAuthRealm() {
        OAuthRealm oAuthRealm = new OAuthRealm();
        // 不需要密码验证
        //oAuthRealm.setCredentialsMatcher(credentialsMatcher());
        return oAuthRealm;
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setAuthenticator(modularRealmAuthenticator());
        List<Realm> realms = new ArrayList<>();
        // 用户登录
        realms.add(userRealm());
        // 第三方登陆
        realms.add(OAuthRealm());
        securityManager.setRealms(realms);
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }
    /**
     * 系统自带的Realm管理，主要针对多realm
     * */
    @Bean
    public ModularRealmAuthenticator modularRealmAuthenticator(){
        //自己重写的ModularRealmAuthenticator
        UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return modularRealmAuthenticator;
    }

    @Bean(name = "webShiroCacheManager")
    public EhCacheManager cacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:shiro/web-ehcache-shiro.xml");
        return cacheManager;
    }

    /*@Bean(name = "securityManager")
    public SecurityManager securityManager(@Qualifier("userRealm") UserRealm authRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(authRealm);
        securityManager.setCacheManager(redisCacheManager());
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        // 注入记住我管理器
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }
    *//**
     * shiro session的管理
     *//*
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(redisProperties.getExpire() * 1000L);
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }
    *//**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     *
     * @return
     *//*
    public RedisManager redisManager() {
        System.out.println("##############"+appConfig.isEnableKaptcha());
        CustomRedisManager redisManager = new CustomRedisManager();
        redisManager.setHost(redisProperties.getHost());
        redisManager.setPort(redisProperties.getPort());
        redisManager.setDatabase(redisProperties.getDatabase());
        redisManager.setExpire(redisProperties.getExpire());
        redisManager.setTimeout(redisProperties.getTimeout().getNano() * 1000);
        redisManager.setPassword(redisProperties.getPassword());
        return redisManager;
    }

    *//**
     * cacheManager 缓存 redis实现
     * 使用的是shiro-redis开源插件
     *
     * @return
     *//*
    @Bean
    public RedisCacheManager redisCacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }


    *//**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     *//*
//    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }
    *//**
     * cookie对象;
     *
     * @return
     *//*
    public SimpleCookie rememberMeCookie() {
        // 这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        // 记住我cookie生效时间30天 ,单位秒。 注释掉，默认永久不过期 2018-07-15
        simpleCookie.setMaxAge(redisProperties.getExpire());
        return simpleCookie;
    }

    *//**
     * cookie管理对象;记住我功能
     *
     * @return
     *//*
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("1QWLxg+NYmxraMoxAXu/Iw=="));
        return cookieRememberMeManager;
    }*/
    /**
     *    凭证匹配器
     *   （由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     *    所以我们需要修改下doGetAuthenticationInfo中的代码;
     * @return
     */
    @Bean(name = "credentialsMatcher")
    public HashedCredentialsMatcher credentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("md5");  //散列算法
        credentialsMatcher.setHashIterations(3);  //散列次数
        return credentialsMatcher;
    }

    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        LifecycleBeanPostProcessor lifecycleBeanPostProcessor = new LifecycleBeanPostProcessor();
        return lifecycleBeanPostProcessor;
    }

    /**
     * shiro里实现的Advisor类,用来拦截注解的方法 .
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager());
        return advisor;
    }

    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * ShiroDialect，为了在thymeleaf里使用shiro的标签的bean
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }
}
